package com.an.framework;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.an.framework.security.deal.MyLoginSuccessHandler;
import com.an.framework.security.deal.MyLogoutSuccessHandler;
import com.an.framework.security.user.MyUserDetailServices;

/**
 * @author Administrator
 *
 *         这个配置在应用程序中创建了一个SpringSecurityFilterChain的servlet个过滤器，此过滤器 负责所有的安全。
 *         WebSecurityConfigurerAdapter注销功能会自动应用，默认访问/logout将注销登陆的用户： 使Session无效
 *         清除所有已经配置的RemberMe认证 清除SecurityContextHolder 跳转到登录的页面
 */
@ComponentScan("com.an") // 启用组件扫描

@Configuration
@EnableWebSecurity//此注解可以替换为EnableWebSecurity
@PropertySource(value = "classpath:security/about-security.properties")
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

	private static final Logger log = LoggerFactory.getLogger(SpringSecurityConfig.class);
	
	/**
	 * csrfTokenApicsrfTokenApi:.
	 * 
	 * @since JDK 1.8
	 */
	@Value("${security.csrfTokenApi}")
	private String csrfTokenApi;

	/**
	 * loginApi:loginApi.
	 * 
	 * @since JDK 1.8
	 */
	@Value("${security.loginApi}")
	private String loginApi;

	/**
	 * logoutApi:logoutApi.
	 * 
	 * @since JDK 1.8
	 */
	@Value("${security.logoutApi}")
	private String logoutApi;

	/**
	 * myUserDetailServices:用户接口.
	 * 
	 * @since JDK 1.8
	 */
	@Autowired
	@Qualifier("nameddddddd")
	private MyUserDetailServices myUserDetailServices;
	
	/* 配置Spring security的Filter链
	 * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.WebSecurity)
	 */
	@Override
	public void configure(WebSecurity web) throws Exception {
		super.configure(web);
	}

	/**
	 * passwordEncoder:密码加密.
	 * 
	 * @since JDK 1.8
	 */
	@Autowired
	private PasswordEncoder passwordEncoder;

	/**
	 * pspc:PropertySourcesPlaceholderConfigurer这个Bean是你能否使用${}占位符的关键. <br/>
	 * //要想使用@Value 用${}占位符注入属性，这个bean是必须的，这个就是占位bean,另一种方式是不用value直接
	 * 用Envirment变量直接getProperty('key') .<br/>
	 *
	 * @author atc
	 * @return
	 * @since JDK 1.8
	 */
	@Bean
	public static PropertySourcesPlaceholderConfigurer pspc() {
		return new PropertySourcesPlaceholderConfigurer();
	}

	/* 配置User detail服务
	 * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder)
	 */
	@Autowired
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		log.debug("注入myUserDetailServices到AuthenticationManagerBuilder");
		auth.userDetailsService(myUserDetailServices).passwordEncoder(passwordEncoder);
	}

	// 完成对哪些资源进行认证的描述
	// 对每一个请求进行细粒度安全性控制的关键在于重载这个方法
	// 1、配置如何通过拦截器保护请求
	
	/*
	 * super.configure(http)默认配置： 确保我们的应用中的所有请求都需要用户被认正。 允许用户进行基于表单的认证。
	 * 
	 * 
	 * java配置的and()方法相当于XML标签的关闭
	 * 
	 * 不具体的路径放到最后，具体的路径放到前面，否则就会因为顺序的问题，而产生安全问题。
	 * 
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.csrf().disable();
		http.authorizeRequests()
		    .antMatchers("/user/login.html")
		    .permitAll()
		    .antMatchers("/assets/**")
		    .permitAll()
		    .antMatchers("/admin/**")
		    .hasAuthority("ADMIN")
		    .antMatchers("/**")
		    .hasAuthority("USER")
			.and()
			.formLogin()
			.loginProcessingUrl("/login.do")
			.loginPage("/user/login.html")
			.usernameParameter("username")
			.passwordParameter("password")
			.successHandler(new MyLoginSuccessHandler())
			.failureUrl("/user/login.html")
			.and().logout().logoutUrl(logoutApi)
			.logoutSuccessHandler(new MyLogoutSuccessHandler());
		
	}

	@Bean
	public PasswordEncoder getPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Override
	public String toString() {
		return "SpringSecurityConfig [csrfTokenApi=" + csrfTokenApi + ", loginApi=" + loginApi + ", logoutApi="
				+ logoutApi + "]";
	}

}
